# 导入SQLAlchemy的核心组件
# create_engine: 用于创建数据库连接引擎
# text: 用于将普通字符串标记为可执行的SQL语句，有助于防止SQL注入
from sqlalchemy import create_engine, text
# declarative_base: 是一个工厂函数，用于创建ORM模型的基础类(Base)
from sqlalchemy.ext.declarative import declarative_base
# sessionmaker: 用于创建数据库会话(Session)的工厂
# scoped_session: 提供线程安全的Session
from sqlalchemy.orm import sessionmaker, scoped_session
# contextmanager: 一个装饰器，用于轻松创建上下文管理器（即'with'语句的目标）
from contextlib import contextmanager
# 从我们的配置模块中导入数据库配置
from config.database import DB_CONFIG

# 创建所有ORM模型都必须继承的基础类
# 我们的User, Post等模型都需要继承这个Base
Base = declarative_base()

class DatabaseManager:
    """
    数据库管理器类，采用单例模式。
    确保在整个应用程序生命周期中，只有一个DatabaseManager实例存在，
    从而只有一个数据库连接池（Engine）被创建和管理。
    """
    # _instance是一个类变量，用于存储唯一的实例
    _instance = None
    
    # __new__是Python中创建实例的第一个方法，在__init__之前调用
    def __new__(cls):
        # 如果_instance为None，说明是第一次创建实例
        if cls._instance is None:
            # 调用父类的__new__方法来真正创建一个实例
            cls._instance = super(DatabaseManager, cls).__new__(cls)
            # 调用_initialize方法来设置数据库连接
            cls._instance._initialize()
        # 返回存储的唯一实例
        return cls._instance
    
    def _initialize(self):
        """初始化数据库连接。"""
        # 从配置中获取mysql的详细设置
        db_config = DB_CONFIG['mysql']
        
        # 构建数据库连接URL，格式为：'数据库类型+驱动://用户名:密码@主机:端口/数据库名'
        connection_string = f"{db_config['driver']}://{db_config['username']}:{db_config['password']}@{db_config['host']}:{db_config['port']}/{db_config['database']}"
        
        # 创建数据库引擎(Engine)，这是SQLAlchemy的核心接口
        # Engine管理着一个数据库连接池，负责与数据库的通信
        self.engine = create_engine(
            connection_string,
            pool_size=db_config['pool_size'],           # 连接池中的连接数
            pool_recycle=db_config['pool_recycle'],     # 连接回收时间（秒），防止连接因超时失效
            pool_timeout=db_config['pool_timeout'],     # 获取连接的超时时间
            max_overflow=db_config['max_overflow'],   # 连接池已满时，允许额外创建的连接数
            echo=db_config['echo']                      # 是否打印执行的SQL语句，便于调试
        )
        
        # 创建一个Session的工厂，它知道如何与我们的引擎通信
        self.session_factory = sessionmaker(bind=self.engine)
        
        # 创建一个线程安全的Session代理。在Web应用中，每个请求线程都会得到一个独立的Session
        self.Session = scoped_session(self.session_factory)
    
    def execute_sql(self, sql, params=None):
        """执行原生SQL查询"""
        # engine.connect()会从连接池中获取一个连接，并返回一个Connection对象
        # 使用'with'语句可以确保连接在使用后被正确关闭并返回连接池
        with self.engine.connect() as connection:
            # connection.execute()执行SQL语句
            # text(sql)将字符串标记为安全的SQL
            # params是传递给SQL语句的参数，可以防止SQL注入
            result = connection.execute(text(sql), params or {})
            return result

    def create_tables(self):
        """根据我们定义的模型（如User, Post）在数据库中创建对应的表。"""
        # Base.metadata包含了所有继承自Base的模型的元信息
        # create_all会检查表是否存在，只创建不存在的表
        Base.metadata.create_all(self.engine)
    
    def drop_tables(self):
        """删除所有定义的表，主要用于测试。"""
        Base.metadata.drop_all(self.engine)
    
    @contextmanager
    def session_scope(self):
        """
        提供一个事务范围的会话上下文管理器。
        这是我们事务装饰器的核心。
        """
        # 从线程安全的Session代理中获取一个Session实例
        session = self.Session()
        try:
            # 'yield'将session对象返回给'with'语句块
            yield session
            # 如果'with'块中没有发生异常，则提交事务
            session.commit()
        except Exception as e:
            # 如果发生任何异常，则回滚事务，撤销所有更改
            session.rollback()
            # 重新抛出异常，以便上层代码可以捕获并处理
            raise e
        finally:
            # 无论成功还是失败，最后都要关闭Session，将其归还给scoped_session管理
            session.close()

# 创建DatabaseManager的单例，整个应用都将使用这个db_manager实例
db_manager = DatabaseManager()